#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include "vxi_ii.h"

#define VXI_SH_PATH		"/tmp/suin/"
#define VXI_CMD_SHM		VXI_SH_PATH"/vxi_ii_cmd.shm"
#define VXI_RSP_SHM		VXI_SH_PATH"/vxi_ii_rsp.shm"
#define VXI_CMD_SEM		VXI_SH_PATH"/vxi_ii_cmd.sem"
#define VXI_RSP_SEM		VXI_SH_PATH"/vxi_ii_rsp.sem"
#define VXI_CMD_SHM_PROJ_CHAR		'a'
#define VXI_RSP_SHM_PROJ_CHAR		'b'
#define VXI_CMD_SEM_PROJ_CHAR		'c'
#define VXI_RSP_SEM_PROJ_CHAR		'd'

char *cmd_shm_addr;
char *rsp_shm_addr;
key_t cmd_shm_key;
key_t rsp_shm_key;
key_t cmd_sem_key;
key_t rsp_sem_key;
int cmd_shmid;
int rsp_shmid;
int cmd_semid;
int rsp_semid;
struct sembuf cmd_sem_p;
struct sembuf cmd_sem_v;
struct sembuf rsp_sem_p;
struct sembuf rsp_sem_v;

static int cmd_shm_lock();
static int cmd_shm_unlock();
static int rsp_shm_lock();
static int rsp_shm_unlock();
static void touch(const char *path);

int vxi_ii_w_cmd_s(const char *cmd_buf, struct timeval *tv)
{
	int data_len;
	int w_numb;
	data_len = strlen(cmd_buf);
	w_numb = vxi_ii_w_cmd(cmd_buf, tv, data_len);
	return w_numb;
}

int vxi_ii_w_cmd(const char *cmd_buf, struct timeval *tv, size_t size)
{
	int data_len;
	int w_numb;
	int ret = 0;
	BOOL non_timeout = 1;
	struct timeval tv_begain;
	struct timeval tv_current;

	gettimeofday(&tv_begain, NULL);
	if(cmd_buf == NULL || tv == NULL){
		return RET_ERR_NULL_ARGU;
	}

	data_len = size;
	if(data_len >= CMD_SHM_SIZE - sizeof(int)){
		return RET_ERR_BUF_OV;
	}

	do{
		if(cmd_shm_lock() == 0){
			break;
		}
		gettimeofday(&tv_current, NULL);
		if(((tv_current.tv_sec - tv_begain.tv_sec) > tv->tv_sec) \
			&&((tv_current.tv_usec - tv_begain.tv_usec) > tv->tv_usec)){
			non_timeout = 0;
		}
	}while(non_timeout && ret);

	if(non_timeout){
		memcpy(cmd_shm_addr + sizeof(int), cmd_buf, data_len);
		*(int *)cmd_shm_addr = data_len;
		w_numb = data_len;
	}

	cmd_shm_unlock();
	if(non_timeout){
		printf("write data numb = %d\n", w_numb);
		return w_numb;
	}
	else{
		printf("write timeout!\n");
		return RET_ERR_TIMEOUT;
	}
}

int vxi_ii_r_cmd(char *cmd_buf, size_t size)
{
	int data_len;
	int r_numb = 0;
//	int ret = 0;

	if(cmd_buf == NULL){
		return RET_ERR_NULL_ARGU;
	}

	if(*(int *)cmd_shm_addr < 1){
		return 0;
	}
	if(cmd_shm_lock() == -1){
		return 0;
	}
	data_len = *(int *)cmd_shm_addr;
	if(data_len < size){
		memcpy(cmd_buf, cmd_shm_addr + sizeof(int), data_len);
		*(int *)cmd_shm_addr = -1;
		r_numb = data_len;
	}
	else{
		memcpy(cmd_buf, cmd_shm_addr + sizeof(int), size);
		memmove(cmd_buf, cmd_shm_addr + sizeof(int) + data_len, data_len - size);
		*(int *)cmd_shm_addr = data_len - size;
		r_numb = size;
	}
	cmd_shm_unlock();

	return r_numb;
}

int vxi_ii_w_rsp_s(const char *resp_buf)
{
	int data_len;
	int w_numb;
	data_len = strlen(resp_buf);
	w_numb = vxi_ii_w_rsp(resp_buf, data_len);
	return w_numb;
}

int vxi_ii_w_rsp(const char *resp_buf, size_t size)
{
	int data_len;
	int w_numb;
//	int ret = 0;

	if(resp_buf == NULL){
		return RET_ERR_NULL_ARGU;
	}

	data_len = size;
	if(data_len >= CMD_SHM_SIZE - sizeof(int)){
		return RET_ERR_BUF_OV;
	}

	if(rsp_shm_lock() == -1){
		return -1;
	}

	memcpy(rsp_shm_addr + sizeof(int), resp_buf, data_len);
	*(int *)rsp_shm_addr = data_len;
	w_numb = data_len;

	rsp_shm_unlock();
	return w_numb;
}

int vxi_ii_r_rsp(char *resp_buf, struct timeval *tv, size_t size)
{
	int data_len;
	int r_numb = 0;
//	int ret = 0;
	BOOL non_timeout = 1;
	struct timeval tv_begain;
	struct timeval tv_current;

	gettimeofday(&tv_begain, NULL);
	if(resp_buf == NULL || tv == NULL){
		return RET_ERR_NULL_ARGU;
	}

	while(non_timeout){
		if(*(int *)rsp_shm_addr > 0){
			if(rsp_shm_lock() == 0){
				break;
			}
		}
		gettimeofday(&tv_current, NULL);
		if(((tv_current.tv_sec - tv_begain.tv_sec) > tv->tv_sec) \
			&&((tv_current.tv_usec - tv_begain.tv_usec) > tv->tv_usec)){
			non_timeout = 0;
		}
	}

	if(non_timeout){
		data_len = *(int *)rsp_shm_addr;
		if(data_len < size){
			memcpy(resp_buf, rsp_shm_addr + sizeof(int), data_len);
			*(int *)rsp_shm_addr = -1;
			r_numb = data_len;
		}
		else{
			memcpy(resp_buf, rsp_shm_addr + sizeof(int), size);
			memmove(resp_buf, rsp_shm_addr + sizeof(int) + data_len, data_len - size);
			*(int *)rsp_shm_addr = data_len - size;
			r_numb = size;
		}
	}

	rsp_shm_unlock();
	if(non_timeout){
		printf("read data numb = %d\n", r_numb);
		return r_numb;
	}
	else{
		printf("read timeout!\n");
		return RET_ERR_TIMEOUT;
	}
}

int cmd_sem_init()
{
	cmd_sem_key = ftok(VXI_CMD_SEM, VXI_CMD_SEM_PROJ_CHAR);
	cmd_semid = semget(cmd_sem_key, 1, 0777 | IPC_CREAT | IPC_NOWAIT);
	if(cmd_semid < 0){
		printf("creat cmd sem error!\n");
		return -1;
	}
	int initval[1] = {1};
	semctl(cmd_semid, 1, SETALL, initval);
	cmd_sem_p.sem_op = -1;
	cmd_sem_p.sem_flg = SEM_UNDO;
	cmd_sem_p.sem_num = 0;
	cmd_sem_v.sem_op = 1;
	cmd_sem_v.sem_flg = SEM_UNDO;
	cmd_sem_v.sem_num = 0;

	return 0;
}

int rsp_sem_init()
{
	rsp_sem_key = ftok(VXI_RSP_SEM, VXI_RSP_SEM_PROJ_CHAR);
	rsp_semid = semget(rsp_sem_key, 1, 0777 | IPC_CREAT | IPC_NOWAIT);
	if(rsp_semid < 0){
		printf("creat rsp sem error!\n");
		return -1;
	}
	int initval[1] = {1};
	semctl(rsp_semid, 1, SETALL, initval);
	rsp_sem_p.sem_op = -1;
	rsp_sem_p.sem_flg = SEM_UNDO;
	rsp_sem_p.sem_num = 0;
	rsp_sem_v.sem_op = 1;
	rsp_sem_v.sem_flg = SEM_UNDO;
	rsp_sem_v.sem_num = 0;

	return 0;
}

int cmd_shm_init()
{
	int ret = 0;
	struct stat t_stat;
	if(((ret = stat(VXI_SH_PATH, &t_stat)) == -1) || (!S_ISDIR(t_stat.st_mode))){
		mkdir(VXI_SH_PATH, S_IREAD | S_IWRITE);
	}
    touch(VXI_CMD_SHM);
	cmd_shm_key = ftok(VXI_CMD_SHM, VXI_CMD_SHM_PROJ_CHAR);
	if(cmd_shm_key < 0){
		printf("cmd shm ftok error: %s!\n", strerror(errno));
		return -1;
	}
	cmd_shmid = shmget(cmd_shm_key, CMD_SHM_SIZE, 0777 | IPC_CREAT);
	cmd_shm_addr = shmat(cmd_shmid, 0, 0);
	*(int *)cmd_shm_addr = -1;

	return 0;
}

int rsp_shm_init()
{
	int ret = 0;
	struct stat t_stat;
	if(((ret = stat(VXI_SH_PATH, &t_stat)) == -1) || (!S_ISDIR(t_stat.st_mode))){
		mkdir(VXI_SH_PATH, S_IREAD | S_IWRITE);
	}
    touch(VXI_RSP_SHM);
	rsp_shm_key = ftok(VXI_RSP_SHM, VXI_RSP_SHM_PROJ_CHAR);
	if(rsp_shm_key < 0){
		printf("rsp shm ftok error: %s!\n", strerror(errno));
		return -1;
	}
	rsp_shmid = shmget(rsp_shm_key, RSP_SHM_SIZE, 0777 | IPC_CREAT);
	rsp_shm_addr = shmat(rsp_shmid, 0, 0);
	*(int *)rsp_shm_addr = -1;

	return 0;
}

static int cmd_shm_lock()
{
	return semop(cmd_semid, &cmd_sem_p, 1);
}

static int cmd_shm_unlock()
{
	return semop(cmd_semid, &cmd_sem_v, 1);
}

static int rsp_shm_lock()
{
	return semop(rsp_semid, &rsp_sem_p, 1);
}

static int rsp_shm_unlock()
{
	return semop(rsp_semid, &rsp_sem_v, 1);
}

static void touch(const char *path)
{
    // 尝试以写模式打开文件（存在则打开，不存在则创建）
    int fd = open(path, O_WRONLY | O_CREAT, 0666);
    if (fd == -1) {
        perror("open");
        return;
    }
    close(fd);

    // 更新时间戳为当前时间
    if (utime(path, NULL) == -1) {
        perror("utime");
    }
}

